package com.hmsm.server.base;

import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;

import javax.annotation.Resource;

import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.metadata.ClassMetadata;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.orm.hibernate3.LocalSessionFactoryBean;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;

import com.lzyyj.common.BeanUtils;
import com.lzyyj.common.GenericsUtils;

public abstract class AbstractBaseDao<T, ID extends Serializable> extends HibernateDaoSupport implements ApplicationContextAware {

	private static final Log log = LogFactory.getLog(AbstractBaseDao.class);
	
	private static ApplicationContext applicationContext;
	
	@Resource
	public void setSessionFactoryOverride(SessionFactory sessionFactory){  
		super.setSessionFactory(sessionFactory);  
	}

	public Session getCurrentSession(){
		Session session;
		session = getHibernateTemplate().getSessionFactory().openSession();
		return session;
	}
	
	@Override
	public void setApplicationContext(ApplicationContext context) throws BeansException {
		applicationContext = context;
	}

	private static Configuration configuration;
	
	public Class<T> persistenceClass;
	
	protected Class<ID> pkClass;

	public AbstractBaseDao() {
		parseEntityClass(null);
	}
	
	@SuppressWarnings("unchecked")
	private void parseEntityClass(Class<T> entityClass) {
		String description;
		if (entityClass==null){
			this.persistenceClass = ((Class<T>)GenericsUtils.getSuperClassGenricType(getClass(), AbstractBaseDao.class, "T"));
			description = "默认构造";
		}else{
			this.persistenceClass = entityClass;
			
			description = "带参构造";
		}
		pkClass = ((Class<ID>)GenericsUtils.getSuperClassGenricType(getClass(), AbstractBaseDao.class, "ID"));
		log.debug(getClass().getSimpleName()+" "+description+"，"+" 管理实体: "+this.persistenceClass+" 主键："+pkClass+" ");
	}
	
	public final Class<T> getPersistenceClass() {
		return persistenceClass;
	}

	public final Class<ID> getPkClass() {
		return this.pkClass;
	}
	
	public String getTableName(){
		return getTableName(persistenceClass);
	}

	/**
	 * 判断对象某些属性的值在数据库中是否唯一.
	 * 
	 * @param propertyList
	 *			在POJO里不能重复的属性列表,以逗号分割 如"name,loginid,password"
	 */
	public boolean isUnique(T entityVo, String propertyList) {
		boolean result;
		Assert.hasText(propertyList);
		Session session = getHibernateTemplate().getSessionFactory().openSession();
		Criteria criteria = session.createCriteria(persistenceClass, "this_").setProjection(Projections.rowCount());
		String[] nameList = propertyList.split(",");
		try {
			// 循环加入唯一列
			for (String name : nameList) {
				Object value = PropertyUtils.getProperty(entityVo, name);
				if (value == null) {
					criteria.add(Restrictions.isNull(name));
				} else {
					criteria.add(Restrictions.eq(name, value));
				}
			}

			// 以下代码为了如果是update的情况,排除entity自身.

			String idName = getPKName();

			// 取得entity的主键值
			Serializable id = getPK(entityVo);

			// 如果id!=null,说明对象已存在,该操作为update,加入排除自身的判断
			if (id != null)
				criteria.add(Restrictions.not(Restrictions.eq(idName, id)));
		} catch (Exception e) {
			ReflectionUtils.handleReflectionException(e);
		}
		result = (Integer) criteria.uniqueResult() == 0;
		session.close();
		
		return result;
	}

	/**
	 * 取得对象的主键名,辅助函数.
	 */
	public String getPKName() {
		String pkName = getPkNameByEntityClass(persistenceClass);
		return pkName;
	}

	/**
	 * 取得指定实体类的主键属性名,辅助函数.
	 * @return
	 */
	public String getPkNameByEntityClass(Class<?> entityClass) {
		Assert.notNull(entityClass);
		ClassMetadata meta = getSessionFactory().getClassMetadata(entityClass);
		Assert.notNull(meta, "Class " + entityClass + " not define in hibernate session factory.");
		String idName = meta.getIdentifierPropertyName();
		Assert.hasText(idName, entityClass.getSimpleName() + " has no identifier property define.");
		return idName;
	}

	/**
	 * 取得对象的主键值,辅助函数.
	 */
	public ID getPK(T entity) {
		Assert.notNull(entity);
		Assert.notNull(persistenceClass);
		ID pk = null;
		try {
			pk = (ID) PropertyUtils.getProperty(entity, getPKName());
		} catch (Exception e) {
			pk = null;
		}

		return pk;
	}

	public T getNew(){
		try {
			return persistenceClass.newInstance();
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}
	
	/** 
	 * 获得实体类对应的表名 
	 *  
	 * @param clazz 
	 *			实体类的Class对象 
	 * @return 表名 
	 */  
	public static <T> String getTableName(Class<T> clazz) {  
		return getPersistentClass(clazz).getTable().getName();  
	}
	
	private static <T> PersistentClass getPersistentClass(Class<T> clazz) {  
		synchronized (AbstractBaseDao.class) {  
			PersistentClass pc = getConfiguration().getClassMapping(clazz.getName());  
			if (pc == null) {  
				configuration = configuration.addClass(clazz);  
				pc = configuration.getClassMapping(clazz.getName());  
			}  
			return pc;  
		}  
	}
	
	public static Configuration getConfiguration() {  
		  
		if (configuration == null) {   
			// 取sessionFactory的时候要加上&  
			LocalSessionFactoryBean factory = (LocalSessionFactoryBean) applicationContext  
					.getBean("&sessionFactory");  
			configuration = factory.getConfiguration();  
		}  
  
		return configuration;  
	} 

	/**
	 * 
	 *  @描述:获取持久化对象所有的持久化属性名称和属性值的键值对
	 *  @方法名称:getPersistencePropertyMap
	 *  @作者: lzyyj
	 *  @创建日期:2018-12-5 下午21:18 
	 *  @param entityObject
	 *  @return Map<String, Object>
	 *
	 */
	public Map<String, Object> getPersistencePropertyMap(Object entityObject) {
		Map<String, Object> propertiesMap = new HashMap<String, Object>();
		
		if(entityObject!=null) {
			Class<?> entityClass = entityObject.getClass();
			ClassMetadata meta = getSessionFactory().getClassMetadata(entityClass);
			for (String propertyName : meta.getPropertyNames()) {
				Object value = null;
				try {
					value = BeanUtils.getPropertyValue(entityObject, propertyName);;
				} catch (Exception e) {
					e.printStackTrace();
				}
				if (value != null) {
					propertiesMap.put(propertyName, value);
				}
			}
		}
		return propertiesMap;
	}

}
